"""
正则表达式
1、正则表达式又称规则表达式，是使用单个字符串来描述、匹配某个句法规则的字符串，常被用来检索、替换匹配到的规则文本
2、简单来说，正则表达式是使用字符串定义规则，通过这个规则去验证字符串是否匹配
3、python中使用内置模块 re 来实现正则匹配
1)导入re模块
import re
2)re.match(匹配规则,被匹配字符串)方法，从被匹配字符串的开头开始匹配，匹配成功则返回匹配数据，匹配不成功则返回None
s = 'hello abc maomao abc'
result = re.match('abc',s) # None 若匹配成功则返回匹配成功的下标范围，若匹配是吧则返回None
3)re.search('匹配规则',匹配字符串)，搜索整个字符串，找到匹配的，从前往后，找到第一个匹配的就停止匹配，匹配不到则返回None
s = 'hello abc maomao abc'
result = re.search('abc',s)
4)re.findall('匹配规则',匹配字符串)，匹配整个字符串，匹配所有符合的字符串，返回列表，匹配不到则返回空列表
s = 'hello abc maomao abc'
result = re.findall('abc',s)
5) result.span() # 返回匹配到的下标
6) result.group() # 返回匹配到的内容
4、元字符匹配
1) . : 匹配任意1个字符（除了\n, \. 匹配点本身）
2) [] : 匹配 [] 中列举的字符
3) \d : 匹配数字，即0-9的数字
4) \D : 匹配非数字
5) \s : 匹配空白，即空格，tab键
6) \S : 匹配非空格
7) \w : 匹配单词字符，即a-z,A-Z,0-9,
8) \W : 匹配非单词字符
5、数量匹配
1) * : 匹配前一个规则的字符出现0-无数次
2) + : 匹配前一个规则的字符出现1-无数次
3) ? : 匹配前一个规则的字符出现0次 或 1次
4) {m} : 匹配前一个规则的字符出现m次
5) {m,} : 匹配前一个规则的字符最少出现m次
6) {m,n} : 匹配前一个规则的字符出现m-n次
6、边界匹配：
1) ^ : 匹配字符串开头
2) $ : 匹配字符串结尾
3) \b : 匹配一个单词的边界
4) \B : 匹配非单词边界
7、分组匹配
1) | : 匹配左右任意一个表达式
2) () : 将括号中字符作为一个分组
8、字符前使用 r 表示字符串内的转义字符无效，只是作为一个普通字符
s = (r'abc\n')


"""

# 导入re模块
import re
s = 'hello abc maomao abc hello'
# ----------match
result = re.match('abc',s)
print(result)
result = re.match('hello',s)
print(result)
# 若没有匹配到数据，即返回None时，不能调用下面两个方法，会报错
print(result.span()) # 匹配到的始末下标
print(result.group()) # 匹配到的字符串数据

# -----------search
s = 'hello abc maomao abc hello'
result = re.search('abc',s)
print(result.span(),result.group())
result = re.search('bca',s)
print(result) # None

# -----------findall
s = 'hello abc maomao abc hello'
result = re.findall('abc',s)
print(result)
result = re.findall('acd',s)
print(result)     # []



# -----------练习
# 匹配账户 只能由数字，字母组成，长度限制6到10位
r = '^[0-9a-zA-Z]{6,10}$'    # 匹配规则
print(re.findall(r,'123'))   # '123' 不满足匹配条件 长度不匹配 返回空列表
print(re.findall(r,'_abc123'))   # '_abc123' 不满足匹配条件 多了下划线 返回空列表
print(re.findall(r,'123456789aa'))     # '123456789aa' 不满足匹配条件 长度不匹配 返回空列表
print(re.findall(r,'1234567aa'))   # '1234567aa' 满足条件 匹配成功

# 匹配QQ号 要求纯数字，长度在5-11位，但是第一位不能位0
r = '^[1-9][0-9]{4,10}$'
print(re.findall(r,'123'))   # '123' 不满足匹配条件 长度不匹配 返回空列表
print(re.findall(r,'0123456789'))   # '123456789' 不满足匹配条件 第一位不能为0 返回空列表
print(re.findall(r,'12345678bb'))   # '12345678bb' 不满足匹配条件 不是纯数字 返回空列表
print(re.findall(r,'12345678'))   # '12345678' 满足条件 匹配成功

# 匹配邮箱地址 只允许qq,163,gmail三种地址的邮箱
# 邮箱格式： 内容.内容.内容@qq.内容.内容.内容
r = r'(^[\w-]+(\.[\w-]+)*@(qq|163|gmail)(\.[\w-]+)+$)'
print(re.match(r,'10646@qq.com'))   # '12345678' 满足条件 匹配成功


str_1 = 'hello abc maomao abc'  # 被匹配的字符串
rule_1 = 'abc'    # 匹配规则
rule_2 = 'hello'

# 若匹配成功则返回匹配成功的下标范围，若匹配失败则返回None
result_1 = re.match(rule_1, str_1)  # 匹配失败 none
result_2 = re.match(rule_2, str_1)  # 匹配成功  <re.Match object; span=(0, 5), match='hello'>
print(result_1)
print(result_2)

# index_1 = result_1.span()  # 匹配返回的结果是none 调用会提示错误 AttributeError: 'NoneType' object has no attribute 'span'
index_2 = result_2.span()    # 匹配成功 调用时返回匹配到的下标值
print(f"匹配到的下标值是：{index_2}")

# text_1 = result_1.group()  # 匹配结果返回的是none ，调用会提示错误 AttributeError: 'NoneType' object has no attribute 'group'
text_2 = result_2.group()    # 匹配成功 调用时返回匹配到的内容
print(f"匹配到的内容是：{text_2}")


str_1 = 'hello abc maomao abc'  # 被匹配的字符串
rule_1 = 'miao'    # 匹配规则
rule_2 = 'abc'
# 若匹配成功则返回匹配成功的下标范围，若匹配失败则返回None
result_1 = re.search(rule_1, str_1)  # 匹配失败 none
result_2 = re.search(rule_2, str_1)  # 匹配成功  <re.Match object; span=(6, 9), match='abc'>
print(result_1)
print(result_2)


str_1 = 'hello abc maomao abc'  # 被匹配的字符串
rule_1 = 'miao'    # 匹配规则
rule_2 = 'abc'
# 若匹配成功则返回匹配成功的下标范围，若匹配失败则返回None
result_1 = re.findall(rule_1, str_1)  # 匹配失败 []
result_2 = re.findall(rule_2, str_1)  # 匹配成功  ['abc', 'abc']
print(result_1)
print(result_2)

print("元字符匹配")
# -----------元字符匹配
s = r'aA4985~!@#$%^&888777*()_+/ *-+.\n\ '
print(re.findall('.',s))
print(re.findall(r'\d',s))    # 匹配数字
print(re.findall(r'\D',s))    # 匹配非数字
print(re.findall(r'\s',s))    # 匹配空白 即空格 tab键
print(re.findall(r'\S',s))    # 匹配非空白 即非空格 tab键
print(re.findall(r'\w',s))    # 匹配单词字符 包含了下划线_ 数字 英文
print(re.findall(r'\W',s))    # 匹配非单词字符 包含了非下划线_ 数字 英文

print("数量匹配")
# -----------数量匹配
s = 'abc1234mm56555'
print(re.findall(r'\d*',s))       # 匹配连续的数字 没匹配到就返回空格
print(re.findall(r'\d+',s))       # 匹配连续的数字 没匹配则不返回
print(re.findall(r'\d?',s))       # 匹配每个字符是否为数字，是则返回对应的数字，不上则返回空格
print(re.findall(r'\d{2}',s))     # 匹配数字出现的次数为2次的内容
print(re.findall(r'\d{2,}',s))    # 匹配长度大于2的字符
print(re.findall(r'\d{3,5}',s))   # 匹配长度限制在3到5位

# ----------边界匹配

# -----------分组匹配
# print(re.findall(r'qq|163','10646.qq'))